package com.bolt.auth.security.common.config;

import com.bolt.auth.security.common.annotation.IgnoreFilter;
import com.bolt.auth.security.common.properties.FilterIgnoreProperties;
import com.bolt.common.utils.SpringContextUtil;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Created by Administrator on 2019/9/24.
 */
@EnableConfigurationProperties(FilterIgnoreProperties.class)
public class BoltResourceServerConfigurer extends ResourceServerConfigurerAdapter {

    @Autowired(required = false)
    private TokenStore tokenStore;

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private OAuth2WebSecurityExpressionHandler expressionHandler;

    @Autowired
    private OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler;


    @Autowired
    private FilterIgnoreProperties filterIgnoreProperties;

    @Override
    @SneakyThrows
    public void configure(HttpSecurity httpSecurity) {
        //允许使用iframe 嵌套，避免swagger-ui 不被加载的问题
        httpSecurity.headers().frameOptions().disable();
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>
                .ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        registry.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() ;//监控断点放权
        //前后分离时需要带上
        registry.antMatchers(HttpMethod.OPTIONS).permitAll();
        registry.antMatchers(filterIgnoreProperties.getIgnored()).permitAll();
        ignoreFilter(registry);
        registry.anyRequest().authenticated().and().csrf().disable();
    }



    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        if (tokenStore != null) {
            resources.tokenStore(tokenStore);
        }
        resources.stateless(true);
        // 自定义异常处理端口
        resources.authenticationEntryPoint(authenticationEntryPoint);
        resources.expressionHandler(expressionHandler);
        resources.accessDeniedHandler(oAuth2AccessDeniedHandler);
    }

    private void ignoreFilter( ExpressionUrlAuthorizationConfigurer<HttpSecurity>
                                       .ExpressionInterceptUrlRegistry registry) {
        RequestMappingHandlerMapping requestMappingHandlerMapping =
                (RequestMappingHandlerMapping) SpringContextUtil.getBean("requestMappingHandlerMapping");
        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
        for (Map.Entry <RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
            HandlerMethod handlerMethod = infoEntry.getValue();
            IgnoreFilter ignoreFilter = handlerMethod.getMethodAnnotation(IgnoreFilter.class);
            if (null != ignoreFilter) {
                List<RequestMethod> requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());
                HttpMethod request = HttpMethod.resolve(requestMethods.get(0).name());
                String[] ignoreUrl = infoEntry.getKey().getPatternsCondition().getPatterns().toArray(new String[0]);
                switch (Objects.requireNonNull(request)) {
                    case GET:
                        registry.antMatchers(HttpMethod.GET, ignoreUrl).permitAll();
                        break;
                    case POST:
                        registry.antMatchers(HttpMethod.POST, ignoreUrl).permitAll();
                        break;
                    case PUT:
                        registry.antMatchers(HttpMethod.PUT, ignoreUrl).permitAll();
                        break;
                    case PATCH:
                        registry.antMatchers(HttpMethod.PATCH, ignoreUrl).permitAll();
                        break;
                    case DELETE:
                        registry.antMatchers(HttpMethod.DELETE, ignoreUrl).permitAll();
                        break;
                    default:
                        registry.antMatchers(ignoreUrl).permitAll();
                        break;
                }
            }
        }
    }
}

 